//
//  IDEA2.c
//  HTHappySale
//
//  Created by 创维海通 on 2017/9/19.
//  Copyright © 2017年 zxyywy. All rights reserved.
//

#include "IDEA2.h"
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define IDEA_INT unsigned int

#define idea_mul(r,a,b,ul) \
ul=(unsigned long)a*b; \
if (ul != 0) \
{ \
r=(ul&0xffff)-(ul>>16); \
r-=((r)>>16); \
} \
else \
r=(-(int)a-b+1); /* assuming a or b is 0 and in range */ \

#define E_IDEA(num) \
x1&=0xffff; \
idea_mul(x1,x1,*p,ul); p++; \
x2+= *(p++); \
x3+= *(p++); \
x4&=0xffff; \
idea_mul(x4,x4,*p,ul); p++; \
t0=(x1^x3)&0xffff; \
idea_mul(t0,t0,*p,ul); p++; \
t1=(t0+(x2^x4))&0xffff; \
idea_mul(t1,t1,*p,ul); p++; \
t0+=t1; \
x1^=t1; \
x4^=t0; \
ul=x2^t0; /* do the swap to x3 */ \
x2=x3^t1; \
x3=ul;

#undef n2s
#define n2s(c,l)	(l =((IDEA_INT)(*((c)++)))<< 8L, \
l|=((IDEA_INT)(*((c)++)))      )

#undef n2l
#define n2l(c,l)        (l =((unsigned long)(*((c)++)))<<24L, \
l|=((unsigned long)(*((c)++)))<<16L, \
l|=((unsigned long)(*((c)++)))<< 8L, \
l|=((unsigned long)(*((c)++))))

#undef l2n
#define l2n(l,c)        (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \
*((c)++)=(unsigned char)(((l)>>16L)&0xff), \
*((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
*((c)++)=(unsigned char)(((l)     )&0xff))

typedef struct idea_key_st
{
    IDEA_INT data[9][6];
} IDEA_KEY_SCHEDULE;

//static char key[] = { 0x39, 0x37, 0x38, 0x31, 0x32, 0x34, 0x36, 0x33, 0x35, 0x30, 0x48, 0x51, 0x53, 0x54, 0x41, 0x52 };
static idea_init_flag = 0;
static IDEA_KEY_SCHEDULE skey, dkey;

static IDEA_INT inverse(unsigned int xin)
{
    long n1, n2, q, r, b1, b2, t;
    
    if (xin == 0)
        b2 = 0;
    else
    {
        n1 = 0x10001;
        n2 = xin;
        b2 = 1;
        b1 = 0;
        
        do
        {
            r = (n1 % n2);
            q = (n1 - r) / n2;
            if (r == 0)
            {
                if (b2 < 0)
                    b2 = 0x10001 + b2;
            }
            else
            {
                n1 = n2;
                n2 = r;
                t = b2;
                b2 = b1 - q * b2;
                b1 = t;
            }
        } while (r != 0);
    }
    return ((IDEA_INT) b2);
}

void idea_set_encrypt_key(unsigned char *key, IDEA_KEY_SCHEDULE *ks)
{
    int i;
    register IDEA_INT *kt, *kf, r0, r1, r2;
    
    kt = &(ks->data[0][0]);
    n2s(key, kt[0]);
    n2s(key, kt[1]);
    n2s(key, kt[2]);
    n2s(key, kt[3]);
    n2s(key, kt[4]);
    n2s(key, kt[5]);
    n2s(key, kt[6]);
    n2s(key, kt[7]);
    
    kf = kt;
    kt += 8;
    for (i = 0; i < 6; i++)
    {
        r2 = kf[1];
        r1 = kf[2];
        *(kt++) = ((r2 << 9) | (r1 >> 7)) & 0xffff;
        r0 = kf[3];
        *(kt++) = ((r1 << 9) | (r0 >> 7)) & 0xffff;
        r1 = kf[4];
        *(kt++) = ((r0 << 9) | (r1 >> 7)) & 0xffff;
        r0 = kf[5];
        *(kt++) = ((r1 << 9) | (r0 >> 7)) & 0xffff;
        r1 = kf[6];
        *(kt++) = ((r0 << 9) | (r1 >> 7)) & 0xffff;
        r0 = kf[7];
        *(kt++) = ((r1 << 9) | (r0 >> 7)) & 0xffff;
        r1 = kf[0];
        if (i >= 5)
            break;
        *(kt++) = ((r0 << 9) | (r1 >> 7)) & 0xffff;
        *(kt++) = ((r1 << 9) | (r2 >> 7)) & 0xffff;
        kf += 8;
    }
}

void idea_set_decrypt_key(IDEA_KEY_SCHEDULE *ek, IDEA_KEY_SCHEDULE *dk)
{
    int r;
    register IDEA_INT *fp, *tp, t;
    
    tp = &(dk->data[0][0]);
    fp = &(ek->data[8][0]);
    for (r = 0; r < 9; r++)
    {
        *(tp++) = inverse(fp[0]);
        *(tp++) = ((int) (0x10000L - fp[2]) & 0xffff);
        *(tp++) = ((int) (0x10000L - fp[1]) & 0xffff);
        *(tp++) = inverse(fp[3]);
        if (r == 8)
            break;
        fp -= 6;
        *(tp++) = fp[4];
        *(tp++) = fp[5];
    }
    
    tp = &(dk->data[0][0]);
    t = tp[1];
    tp[1] = tp[2];
    tp[2] = t;
    
    t = tp[49];
    tp[49] = tp[50];
    tp[50] = t;
}

void idea_init(char *key)
{
    idea_set_encrypt_key(key, &skey);
    idea_set_decrypt_key(&skey, &dkey);
}

void idea_encrypt(unsigned long *d, IDEA_KEY_SCHEDULE *key)
{
    register IDEA_INT *p;
    register unsigned long x1, x2, x3, x4, t0, t1, ul;
    
    x2 = d[0];
    x1 = (x2 >> 16);
    x4 = d[1];
    x3 = (x4 >> 16);
    
    p = &(key->data[0][0]);
    
    E_IDEA(0);
    E_IDEA(1);
    E_IDEA(2);
    E_IDEA(3);
    E_IDEA(4);
    E_IDEA(5);
    E_IDEA(6);
    E_IDEA(7);
    
    x1 &= 0xffff;
    idea_mul(x1, x1, *p, ul);
    p++;
    
    t0 = x3 + *(p++);
    t1 = x2 + *(p++);
    
    x4 &= 0xffff;
    idea_mul(x4, x4, *p, ul);
    
    d[0] = (t0 & 0xffff) | ((x1 & 0xffff) << 16);
    d[1] = (x4 & 0xffff) | ((t1 & 0xffff) << 16);
}

void idea_ecb_encrypt(unsigned char *in, unsigned char *out, IDEA_KEY_SCHEDULE *ks)
{
    unsigned long l0, l1, d[2];
    
    n2l(in, l0);
    d[0] = l0;
    n2l(in, l1);
    d[1] = l1;
    idea_encrypt(d, ks);
    l0 = d[0];
    l2n(l0, out);
    l1 = d[1];
    l2n(l1, out);
    l0 = l1 = d[0] = d[1] = 0;
}

int idea_encrypt_buffer(unsigned char *in, const int inLen, unsigned char *out, char * key)
{
    int n = 0, i;
    unsigned char *tIn, *tOut;
    
//    if (idea_init_flag == 0)
//    {
        idea_init(key);
//        idea_init_flag = 1;
//    }
    
    n = inLen / 8;
    
    tIn = in;
    tOut = out;
    int outlen = 0;
    
    for (i = 0; i < n; i++)
    {
        idea_ecb_encrypt(tIn, tOut, &skey);
        tIn += 8;
        tOut += 8;
        outlen += 8;
    }
    return outlen;
}

void xmlPacketEncrypt(char *in, char *out, int len)
{
    int inLen, i;
    i = inLen = strlen(in);
    
    if (i % 8 != 0)
    {
        inLen = (i / 8 + 1) * 8;
    }
    
    if (inLen > len)
    {
        out[0] = 0;
        return;
    }
    
    for (i; i < inLen; i++)
    {
        in[i] = 0;
    }
    
//    idea_encrypt_buffer(in, inLen, out);
    out[inLen] = 0;
    
    bzero(in, len);
//    base64_encode(out, in, inLen);
    
    //bzero(out, len);
    //memcpy(out, in, strlen(in));
    
    //debug("º”√‹±®Œƒ:%s", out);
}

void xmlPacketDecrypt(char *in, char *out)
{
    int i, n;
    int len = strlen(in);
    char *tmpIn, *tmpOut, *tmpPtr;
    
    if (len % 8 != 0)
    {
        out[0] = 0;
        return;
    }
    
    if (idea_init_flag == 0)
    {
//        idea_init();
        idea_init_flag = 1;
    }
    
    tmpIn = in;
    tmpOut = out;
//    base64_decode(tmpIn, tmpOut);
    
    n = len / 8;
    tmpPtr = tmpIn = malloc(len);
    tmpOut = out;
    
    for (i = 0; i < n; i++)
    {
        idea_ecb_encrypt(tmpOut, tmpIn, &dkey);
        tmpIn += 8;
        tmpOut += 8;
    }
    
    memcpy(out, tmpPtr, len);
    
    free(tmpPtr);
}
